from typing import Literal
from urllib import parse

from django.http import HttpResponse

from administrators.models import Userinfo
from inventory.export_table.scrap_steel import print_rust_angle_steel_table
from inventory.views.export import angle_steel_sale_use_data_list, history_strip_steel_table
from inventory.views.func.datatype import special_all_angle_steel_list, inventory_material_type3, \
    inventory_material_type2, strip_steel_sale_table, rust_angle_steel_list, rust_angle_steel_dict, \
    rust_angle_steel_use, rust_angle_steel_row_data
from inventory.views.func.record import M
from utils.tools import ParamCheck
import psycopg2
from django.db import IntegrityError, transaction
from django.db.models import CharField, F, Q
from django.db.models.functions import Cast
import json
from administrators.views.token_check import verify_token
from django.views.decorators.http import require_http_methods
from utils.enums import SCE
from utils.result import R
from datetime import date
import logging
# ---------------------------------------------------------------------
from inventory.models import MaterialGroup, Material, Record

logger = logging.getLogger('inventory')


# 物料增删改查
@verify_token()
@require_http_methods(['POST'])
def material_crud(request, user_id: str):
    data = json.loads(request.body)
    user_obj = Userinfo.objects.get(id=user_id)
    match data['mode']:
        case 'create':
            try:
                code = ParamCheck.str(data['param']['code'], length=(1, 100))
                name = ParamCheck.str(data['param']['material_name'], length=(1, 100))
                model = ParamCheck.str(data['param']['model'], length=(1, 100))
                group = ParamCheck.int(data['param']['group'])
                set_piece = ParamCheck.int(data['param']['set_piece'])
                theory_scatt_weight = ParamCheck.decimal(data['param']['theory_scatt_weight'])

                operator = user_obj
                enable = True
                Material(code=code,
                         name=name,
                         model=model,
                         whole_piece=0,
                         scatt_piece=0,
                         weight=0,
                         set_piece=set_piece,
                         enable=enable,
                         operate_date=date.today(),
                         group_id=group,
                         operator=operator,
                         theory_scatt_weight=theory_scatt_weight,
                         latest_date=date.today(),
                         theory_weight=0,
                         ).save()
                return R.success('物料创建成功').data()
            except IntegrityError as e:
                if isinstance(e.__cause__, psycopg2.errors.UniqueViolation):
                    return R.error(SCE.no_enum, '物料编码重复').data()
            except MaterialGroup.DoesNotExist:
                return R.error(SCE.no_enum, '该物料所属物料分组不存在').data()
            except Exception as e:
                print(e)
                return R.error(SCE.no_enum, '其他故障').data()

        case 'delete':
            # 该物料处于关闭状态，才能删除
            material_id = ParamCheck.int(data['param']['id'])
            obj = Material.objects.get(id=material_id)
            if obj.enable:
                return R.error(SCE.no_enum, '物料未处于关闭状态，不能删除').data()
            # 删除其下的全部出入库记录
            Record.objects.filter(material=obj).delete()
            logger.info(f'物料删除成功|名称：{obj.name}|id：{obj.id}|用户：{user_obj.username}')
            obj.delete()
            return R.success('物料分组删除成功').data()

        case 'update':
            try:
                material_id = ParamCheck.int(data['param']['id'])
                code = ParamCheck.str(data['param']['code'], length=(1, 100))
                name = ParamCheck.str(data['param']['name'], length=(1, 100))
                model = ParamCheck.str(data['param']['model'], length=(1, 100))
                material_group_id = ParamCheck.int(data['param']['group'])  # 希望迁移到的物料分组
                set_piece = ParamCheck.int(data['param']['set_piece'])
                theory_scatt_weight = ParamCheck.decimal(data['param']['theory_scatt_weight'])
                with transaction.atomic():
                    material_obj = Material.objects.get(id=material_id)
                    m_obj = M(material_obj)
                    want_material_group_obj = MaterialGroup.objects.get(id=material_group_id)  # 想要转到的大类
                    # 若本物料处于关闭状态，则不可进行任何修改
                    if not m_obj.enable:
                        return R.error(SCE.no_enum, '该物料处于关闭状态，不可修改').data()
                    # 如果修改物料编码，查看是否已经重复
                    if code != m_obj.code:
                        if not m_obj.modify_code(code):
                            return R.error(SCE.no_enum, '物料编码重复，不可修改').data()
                    # 如果单支理论重量被修改，那么修改理论重量
                    if material_obj.theory_scatt_weight != theory_scatt_weight:
                        m_obj.theory_scatt_weight = theory_scatt_weight
                        m_obj.theory_weight_modify()
                    # 如果件支数被修改，那么重新计算件支，以及修改记录中的件支
                    if material_obj.set_piece != set_piece:
                        m_obj.modify_set_piece(set_piece)
                    # 如果想要迁移物料
                    if want_material_group_obj != m_obj.group:
                        if not want_material_group_obj.enable:
                            return R.error(SCE.no_enum, '目标物料分组处于关闭状态，不可迁移').data()
                        # 进行迁移
                        m_obj.group = want_material_group_obj
                    # 其余如果修改直接保存
                    m_obj.operator = user_obj
                    m_obj.operate_date = date.today()
                    m_obj.name = name
                    m_obj.model = model
                    m_obj.save_obj()
                return R.success('物料修改成功').data()

            except MaterialGroup.DoesNotExist:
                return R.error(SCE.no_enum, '要迁移到的物料分组不存在').data()

        case 'read':
            material_id = ParamCheck.int(data['param']['id'])
            result = (Material.objects
                      .filter(id=material_id)
                      .annotate(operator=F('operator__username', group=F('group__name')))
                      .values('id', 'name', 'model', 'group', 'whole_piece', 'scatt_piece', 'weight', 'set_piece',
                              'operator', 'enable', operate_date_=Cast('operate_date', CharField())))
            return R.success('物料读取成功').data({result: list(result)})

        case _:
            return R.error(SCE.ParamError).data()


# 物料模糊查询
@verify_token()
@require_http_methods(['POST'])
def material_fuzzy_search(request, user_id: str):
    data = json.loads(request.body)
    # 以下三个字段进行模糊搜索
    code = ParamCheck.str(data['code'], length=(0, 100), force=False)
    material_name = ParamCheck.str(data['material_name'], length=(0, 100), force=False)
    model = ParamCheck.str(data['model'], length=(0, 100), force=False)
    # 按照是否启用进行搜索
    enable = ParamCheck.str(data['enable'])
    # 允许指定所属分组进行搜索
    material_group_id = ParamCheck.int(data['material_group_id'], force=False)
    # 正品库库存
    usable_stock_mode = ParamCheck.str(data['usable_stock_mode'])  # 不得为空 ‘all’ | 'zero' | 'not_zero'

    start = ParamCheck.int(data['start'])
    stop = ParamCheck.int(data['stop'])

    result = Material.objects

    if material_group_id is not None:
        result = result.filter(group_id=material_group_id)

    if usable_stock_mode == 'zero':
        result = result.filter(whole_piece=0, scatt_piece=0)
    if usable_stock_mode == 'not_zero':
        result = result.filter(Q(whole_piece__gt=0) | Q(scatt_piece__gt=0))
    if code is not None:
        result = result.filter(code__contains=code)
    if material_name is not None:
        result = result.filter(name__contains=material_name)
    if model is not None:
        result = result.filter(model__contains=model)
    if enable == 'enable':
        result.filter(enable=True)
    if enable == 'not_enable':
        result.filter(enable=False)

    result = result \
        .annotate(operator_name=F('operator__username'), material_group_name=F('group__name'),
                  material_group_id=F('group__id')) \
        .values('id', 'code', 'name', 'model', 'material_group_id', 'material_group_name', 'whole_piece',
                'scatt_piece', 'weight', 'set_piece', 'operator_name', 'enable', 'theory_weight', 'theory_scatt_weight',
                operate_date_=Cast('operate_date', CharField()),
                latest_date_=Cast('latest_date', CharField()))

    total = result.count()
    result = result[start: stop]

    return R.success('物料模糊搜索成功').data({'result': list(result), 'total': total})


# 锈角盘库调整出入库用
@verify_token()
@require_http_methods(['POST'])
def angle_steel_store_modify_only(request, user_id: str):
    data = json.loads(request.body)
    actual_date: date = ParamCheck.date(data.get('export_date'))
    p_data: special_all_angle_steel_list = angle_steel_sale_use_data_list(False, actual_date)
    m_data: special_all_angle_steel_list = angle_steel_sale_use_data_list(True, actual_date)


# 角钢库存表盘库调整用
@verify_token()
@require_http_methods(['POST'])
def angle_steel_store_modify_only(request, user_id: str):
    data = json.loads(request.body)
    actual_date: date = ParamCheck.date(data.get('export_date'))
    p_data: special_all_angle_steel_list = angle_steel_sale_use_data_list(False, actual_date)
    m_data: special_all_angle_steel_list = angle_steel_sale_use_data_list(True, actual_date)

    # 返回一个型号的材料
    def return_model(t_data: special_all_angle_steel_list, t_model: str, t_: list[str], is_m: bool = False) -> list[
        inventory_material_type2]:
        result: list[inventory_material_type2] = []
        if is_m:
            manganese = 'M'
        else:
            manganese = ''
        # 查验该物料是否存在，不存在就直接返回
        if (t_model + t_[0]) not in t_data:
            return result
        # 所有要写入的位置
        temp_result: inventory_material_type2 = {'model': '', 'set_piece': 0, 'total': 0,
                                                 'A': 0, 'B': 0, 'C': 0, 'D': 0, 'G': 0,
                                                 'A_id': 0, 'B_id': 0, 'C_id': 0, 'D_id': 0, 'G_id': 0}
        row_number = len(t_)  # 确认该规格相同的物料有几个
        for i in range(row_number):
            # 规格
            temp_result['model'] = t_model + t_[i]
            # 支数/件
            temp_result['set_piece'] = t_data[t_model + t_[i]]['set_piece']

            temp_a = t_data[t_model + t_[i]]['material'][(manganese + t_model + t_[i] + 'A').replace("*", "")]
            temp_result['A'] = temp_a['whole_piece']
            temp_result['A_id'] = temp_a['id']
            temp_b = t_data[t_model + t_[i]]['material'][(manganese + t_model + t_[i] + 'B').replace("*", "")]
            temp_result['B'] = temp_b['whole_piece']
            temp_result['B_id'] = temp_b['id']
            temp_c = t_data[t_model + t_[i]]['material'][(manganese + t_model + t_[i] + 'C').replace("*", "")]
            temp_result['C'] = temp_c['whole_piece']
            temp_result['C_id'] = temp_c['id']
            temp_d = t_data[t_model + t_[i]]['material'][(manganese + t_model + t_[i] + 'D').replace("*", "")]
            temp_result['D'] = temp_d['whole_piece']
            temp_result['D_id'] = temp_d['id']
            temp_add = t_data[t_model + t_[i]]['material'][(manganese + t_model + t_[i] + '+').replace("*", "")]
            temp_result['G'] = temp_add['whole_piece']
            temp_result['G_id'] = temp_add['id']
            temp_result['total'] = (temp_a['whole_piece'] + temp_b['whole_piece'] + temp_c['whole_piece'] +
                                    temp_d['whole_piece'] + temp_add['whole_piece'])
            result.append(temp_result)
            temp_result = {'model': '', 'set_piece': 0, 'total': 0, 'A': 0, 'B': 0, 'C': 0, 'D': 0, 'G': 0, 'A_id': 0,
                           'B_id': 0, 'C_id': 0, 'D_id': 0, 'G_id': 0}
        temp_result = {'model': '', 'set_piece': 0, 'total': 0, 'A': 0, 'B': 0, 'C': 0, 'D': 0, 'G': 0, 'A_id': 0,
                       'B_id': 0, 'C_id': 0, 'D_id': 0, 'G_id': 0}
        result.append(temp_result)
        return result

    list_6_10 = ['6', '7', '8', '9', '10']
    list_6_12 = ['6', '7', '8', '9', '10', '11', '12']
    result_: inventory_material_type3 = {'235_1': [], '235_2': [], '235_3': [], '235_4': [], '345_1': [], '345_2': []}
    result_['235_1'] += return_model(p_data, '40*3*', list_6_10)
    result_['235_1'] += return_model(p_data, '40*4*', list_6_10)
    result_['235_1'] += return_model(p_data, '45*4*', list_6_10)
    result_['235_1'] += return_model(p_data, '45*5*', list_6_10)
    result_['235_1'] += return_model(p_data, '50*4*', list_6_12)
    result_['235_1'] += return_model(p_data, '50*5*', list_6_12)
    result_['235_1'] += return_model(p_data, '56*4*', list_6_12)
    result_['235_1'] += return_model(p_data, '56*5*', list_6_12)
    
    result_['235_2'] += return_model(p_data, '63*5*', list_6_12)
    result_['235_2'] += return_model(p_data, '63*6*', list_6_12)
    result_['235_2'] += return_model(p_data, '70*5*', list_6_12)
    result_['235_2'] += return_model(p_data, '70*6*', list_6_12)
    result_['235_2'] += return_model(p_data, '70*7*', list_6_12)
    result_['235_2'] += return_model(p_data, '75*5*', list_6_12)
    result_['235_2'] += return_model(p_data, '75*6*', list_6_12)
    
    result_['235_3'] += return_model(p_data, '75*7*', list_6_12)
    result_['235_3'] += return_model(p_data, '75*8*', list_6_12)
    result_['235_3'] += return_model(p_data, '80*6*', list_6_12)
    result_['235_3'] += return_model(p_data, '80*7*', list_6_12)
    result_['235_3'] += return_model(p_data, '80*8*', list_6_12)
    result_['235_3'] += return_model(p_data, '90*6*', list_6_12)
    result_['235_3'] += return_model(p_data, '90*7*', list_6_12)
    
    result_['235_4'] += return_model(p_data, '90*8*', list_6_12)
    
    result_['345_1'] += return_model(m_data, '63*5*', list_6_12, is_m=True)
    result_['345_1'] += return_model(m_data, '63*6*', list_6_12, is_m=True)
    result_['345_1'] += return_model(m_data, '70*5*', list_6_12, is_m=True)
    result_['345_1'] += return_model(m_data, '70*6*', list_6_12, is_m=True)
    result_['345_1'] += return_model(m_data, '70*7*', ['7', '8', '9', '10'], is_m=True)
    result_['345_1'] += return_model(m_data, '75*5*', list_6_12, is_m=True)
    result_['345_1'] += return_model(m_data, '75*6*', list_6_12, is_m=True)

    result_['345_2'] += return_model(m_data, '80*6*', list_6_12, is_m=True)
    result_['345_2'] += return_model(m_data, '80*7*', list_6_12, is_m=True)
    result_['345_2'] += return_model(m_data, '80*8*', list_6_12, is_m=True)
    result_['345_2'] += return_model(m_data, '90*6*', list_6_12, is_m=True)
    result_['345_2'] += return_model(m_data, '90*7*', list_6_12, is_m=True)
    result_['345_2'] += return_model(m_data, '90*8*', list_6_12, is_m=True)

    return R.success('角钢全部数据返回成功').data(result_)


# 输出废品指定格式
@verify_token()
@require_http_methods(['POST'])
def rust_angle_steel(request, user_id: str):
    data = json.loads(request.body)
    actual_date: date = ParamCheck.date(data.get('export_date'))
    mode: Literal['json', 'table'] = ParamCheck.str(data.get('mode'))
    temp1: list[strip_steel_sale_table] = history_strip_steel_table(actual_date, '锈角钢', True)
    # 将结果转为字典
    data_dict: rust_angle_steel_dict = {}
    for i in temp1:
        data_dict[i['model']] = {'model': i['model'], 'whole_piece': i['whole_piece'], 'id': i['id'], 'code': i['code']}
    result: rust_angle_steel_list = []
    # 生成合适的数据
    for i in rust_angle_steel_use:
        temp: rust_angle_steel_row_data = {}
        for index, k in enumerate(i):
            if k != '':
                temp[f'model_{index+1}'] = data_dict[k]['model']
                temp[f'piece_{index+1}'] = data_dict[k]['whole_piece']
                temp[f'id_{index+1}'] = data_dict[k]['id']
                temp[f'code_{index+1}'] = data_dict[k]['code']
            else:
                temp[f'model_{index+1}'] = ''
                temp[f'piece_{index+1}'] = ''
                temp[f'id_{index+1}'] = -1
                temp[f'code_{index+1}'] = ''
        result.append(temp)
    if mode == 'json':
        return R.success('锈角钢全部数据返回成功').data(result)
    else:
        print('开始打印锈角表格')
        xl = print_rust_angle_steel_table(result, actual_date)
        response = HttpResponse()
        response['content-type'] = 'application/vnd.ms-excel'
        response['Content-Disposition'] = 'attachment;filename=' + parse.quote(f'锈角_{actual_date}.xls')
        response['Access-Control-Expose-Headers'] = 'Content-Disposition'
        xl.save(response)
        return response









